package com.oeandn.db.base.interceptor;

import cn.hutool.core.collection.CollUtil;
import cn.hutool.core.lang.TypeReference;
import cn.hutool.core.map.MapUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.spring.SpringUtil;
import com.baomidou.mybatisplus.core.toolkit.PluginUtils;
import com.baomidou.mybatisplus.core.toolkit.StringPool;
import com.baomidou.mybatisplus.extension.parser.JsqlParserSupport;
import com.baomidou.mybatisplus.extension.plugins.inner.InnerInterceptor;
import com.oeandn.common.context.BaseContextHandler;
import com.oeandn.db.base.model.DataScopeRoleModel;
import com.oeandn.db.base.service.IDataScopeService;
import lombok.extern.slf4j.Slf4j;
import net.sf.jsqlparser.expression.LongValue;
import net.sf.jsqlparser.expression.Parenthesis;
import net.sf.jsqlparser.expression.operators.conditional.AndExpression;
import net.sf.jsqlparser.expression.operators.relational.ExpressionList;
import net.sf.jsqlparser.expression.operators.relational.InExpression;
import net.sf.jsqlparser.expression.operators.relational.ItemsList;
import net.sf.jsqlparser.schema.Column;
import net.sf.jsqlparser.statement.select.PlainSelect;
import net.sf.jsqlparser.statement.select.Select;
import org.apache.ibatis.cache.CacheKey;
import org.apache.ibatis.executor.Executor;
import org.apache.ibatis.mapping.BoundSql;
import org.apache.ibatis.mapping.MappedStatement;
import org.apache.ibatis.mapping.SqlCommandType;
import org.apache.ibatis.session.ResultHandler;
import org.apache.ibatis.session.RowBounds;
import org.springframework.stereotype.Component;

import java.lang.reflect.Method;
import java.sql.SQLException;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

/**
 * @Author：饮水机管理员
 * @Description: 数据权限mybatis拦截器
 * @Date: 2021/9/8 14:01
 */
@Slf4j
@Component
public class DataPermissionInterceptor extends JsqlParserSupport implements InnerInterceptor {

    private static final String userNameSpace = "com.oeandn.system.mapper.UserInfoMapper.selectById";

    /**
     * {@link Executor#query(MappedStatement, Object, RowBounds, ResultHandler, CacheKey, BoundSql)} 操作前置处理
     * <p>
     * 改改sql啥的
     *
     * @param executor      Executor(可能是代理对象)
     * @param ms            MappedStatement
     * @param parameter     parameter
     * @param rowBounds     rowBounds
     * @param resultHandler resultHandler
     * @param boundSql      boundSql
     */
    @Override
    public void beforeQuery(Executor executor, MappedStatement ms, Object parameter, RowBounds rowBounds, ResultHandler resultHandler, BoundSql boundSql) throws SQLException {
        PluginUtils.MPBoundSql mpBs = PluginUtils.mpBoundSql(boundSql);
        mpBs.sql(parserSingle(mpBs.sql(), ms));
    }

    /**
     * 查询
     *
     * @param select
     * @param index
     * @param sql
     * @param obj
     */
    @Override
    protected void processSelect(Select select, int index, String sql, Object obj) {
        MappedStatement ms = PluginUtils.realTarget(obj);
        // 先判断是不是SELECT操作 不是直接过滤
        if (!SqlCommandType.SELECT.equals(ms.getSqlCommandType())) {
            return;
        }
        String namespace = ms.getId();
        if(userNameSpace.equals(namespace)){
            return;
        }
        if(!sql.contains("dept_id")){
            return;
        }
        final Long userId = BaseContextHandler.getUserID();
        if (userId == null) {
            return;
        }
        log.info("userID  {} ,namespace  {}", userId,namespace);
        PlainSelect plainSelect = (PlainSelect) select.getSelectBody();
        //获取执行方法的位置
        dataPermissionHandler(userId, plainSelect);
    }

    /**
     * 数据权限处理
     *
     * @param userId      用户id
     * @param plainSelect sql
     */
    private void dataPermissionHandler(Long userId,  PlainSelect plainSelect) {
        final IDataScopeService iDataScopeService = SpringUtil.getBean(IDataScopeService.class);
        final Map<String, Object> allDataScopeFlagAndData = iDataScopeService.getAllDataScopeFlagAndDataByUserId(userId);
        final Boolean allDataScope = MapUtil.getBool(allDataScopeFlagAndData, IDataScopeService.ALL_DATA_SCOPE_FLAG);
        if (!allDataScope) {
            // 有数据范围
            List<DataScopeRoleModel> roleList = MapUtil.get(allDataScopeFlagAndData, IDataScopeService.ROLE_LIST, new TypeReference<List<DataScopeRoleModel>>() {
            });
            Set<Long> dataScopeList = iDataScopeService.getDateScopeDeptIds(userId, roleList);
            if (CollUtil.isNotEmpty(dataScopeList)) {
                ItemsList itemsList = new ExpressionList(dataScopeList.stream().map(LongValue::new).collect(Collectors.toList()));

                InExpression inExpression = new InExpression(new Column("dept_id"), itemsList);
                if (null == plainSelect.getWhere()) {
                    // 不存在 where 条件
                    plainSelect.setWhere(new Parenthesis(inExpression));
                } else {
                    // 存在 where 条件 and 处理
                    plainSelect.setWhere(new AndExpression(plainSelect.getWhere(), inExpression));
                }
            }
        }
    }



}
